<?php


namespace app\index\controller;
use think\Request;
class Paywx extends Base
{
//$price 订单总金额，$openid 用户openid  ，$orderno 商户系统内部的订单号
    public function weixinPayDataW($price, $openid, $orderno)
    {
        //$request = Request::instance();
        $this->values = array(
            "appid" => config('appid'),               //* 微信分配的公众账号ID（企业号corpid即为此appId）
            "mch_id" => config('mch_id'),           //* 微信支付分配的商户号
            "nonce_str" => self::getNonceStr(),      //* 随机字符串，不长于32位。推荐随机数生成算法
            "sign_type" => "MD5",                    //* 签名类型，目前支持HMAC-SHA256和MD5，默认为MD5
            "body" => "test",                    //* 商品简单描述，该字段须严格按照规范传递，具体请见参数规定
            "out_trade_no" => $orderno,              //* 商户系统内部的订单号,32个字符内、可包含字母, 其他说明见商户订单号
            "total_fee" => $price,                   //* 订单总金额，单位为分，详见支付金额
            //"spbill_create_ip" => $request->ip(),    //* APP和网页支付提交用户端ip，Native支付填调用微信支付API的机器IP
            "openid" => $openid,                     //* openid
            "time_start" => date("YmdHis", time()),         //* 订单生成时间，格式为yyyyMMddHHmmss，如2009年12月25日9点10分10秒表示为20091225091010。其他详见时间规则
            "time_expire" => date("YmdHis", (time() + 600)),   //* 订单有效时间为10分钟  订单失效时间，格式为yyyyMMddHHmmss，如2009年12月27日9点10分10秒表示为20091227091010。其他详见时间规则
            "notify_url" => config("WEIXIN_NOTIFY_URL_W"),   //* 接收微信支付异步通知回调地址，通知url必须为直接可访问的url，不能携带参数。
            "trade_type" => "JSAPI",                         //* 交易类型 取值如下：JSAPI，NATIVE，APP，详细说明见参数规定。
        );

        //微信支付api密钥
        $this->SetSign(config('wx_key'));
		
		
        $postXml = $this->ToXml($this->values);
		
//        halt($postXml);
        $xml = self::postXmlCurl($postXml, "https://api.mch.weixin.qq.com/pay/unifiedorder");
        
        $obj = $this->xmlToArray($xml);

//halt($obj);
        if ($obj['return_code'] == "SUCCESS" && $obj['result_code'] != 'FAIL') {
            $rddata = [
                'appId' => config('appid'),
                'package' => 'prepay_id=' . $obj['prepay_id'],
                'nonceStr' => self::getNonceStr(),
                "signType" => "MD5",
                'timeStamp' => '' . time()
            ];
			
            $sgin = $this->paySign($rddata, config('wx_key'));
            $rddata['paySign'] = $sgin;
            return $rddata;
        } else {
            return $obj;
        }
    }

    private static function postXmlCurl($xml, $url, $useCert = false, $second = 30)
    {
        $ch = curl_init();
        //设置超时
        curl_setopt($ch, CURLOPT_TIMEOUT, $second);
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);//严格校验
        //设置header
        curl_setopt($ch, CURLOPT_HEADER, FALSE);
        //要求结果为字符串且输出到屏幕上
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);

        //post提交方式
        curl_setopt($ch, CURLOPT_POST, TRUE);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
        //运行curl
        $data = curl_exec($ch);
        //返回结果
        if ($data) {
            curl_close($ch);
            return $data;
        } else {
            $error = curl_errno($ch);
            curl_close($ch);
            return "";
        }
    }
    /**
     *
     * 产生随机字符串，不长于32位
     */
    public static function getNonceStr($length = 32)
    {
        $chars = "abcdefghijklmnopqrstuvwxyz0123456789";
        $str = "";
        for ($i = 0; $i < $length; $i++) {
            $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
        }
        return strtoupper($str);
    }


    public function ToXml($data)
    {
        if (!is_array($data)
            || count($data) <= 0) {
            echo("数组数据异常！");
        }

        $xml = "<xml>";
        foreach ($this->values as $key => $val) {
            if (is_numeric($val)) {
                $xml .= "<" . $key . ">" . $val . "</" . $key . ">";
            } else {
                $xml .= "<" . $key . "><![CDATA[" . $val . "]]></" . $key . ">";
            }
        }
        $xml .= "</xml>";
        return $xml;
    }


    public function xmlToArray($xml)
    {
        //将XML转为array
        $array_data = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
        return $array_data;
    }


    public
    static function notify($WEIXIN_KEY, $callback, &$msg)
    {
        //获取通知的数据
        $xml = $GLOBALS['HTTP_RAW_POST_DATA'];
        //如果返回成功则验证签名
        $result = self::Init($xml, $WEIXIN_KEY);

        return call_user_func($callback, $result);
    }


    public
    static function Init($xml, $WEIXIN_KEY)
    {
        $obj = new self();
        $obj->FromXml($xml);
        //fix bug 2015-06-29
        if ($obj->values['return_code'] != 'SUCCESS') {
            return $obj->GetValues();
        }
        $obj->CheckSign($WEIXIN_KEY);
        return $obj->GetValues();
    }


    public
    function FromXml($xml)
    {
        if (!$xml) {
            return "";
        }
        //将XML转为array
        //禁止引用外部xml实体
        libxml_disable_entity_loader(true);
        $this->values = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
        return $this->values;
    }



    /**
     * 设置签名，详见签名生成算法
     * @param string $value
     **/
    private
    function SetSign($WEIXIN_KEY)
    {
        $sign = $this->MakeSign($WEIXIN_KEY);
        $this->values['sign'] = $sign;
        return $sign;
    }

    /**
     * 获取签名，详见签名生成算法的值
     * @return 值
     **/
    private
    function GetSign()
    {
        return $this->values['sign'];
    }

    /**
     * 判断签名，详见签名生成算法是否存在
     * @return true 或 false
     **/
    private
    function IsSignSet()
    {
        return array_key_exists('sign', $this->values);
    }

    /**
     *
     * 检测签名
     */
    private
    function CheckSign($WEIXIN_KEY)
    {
        //fix异常
        if (!$this->IsSignSet()) {
            throw new WxPayException("签名错误！");
        }

        $sign = $this->MakeSign($WEIXIN_KEY);
        if ($this->GetSign() == $sign) {
            return true;
        }
        throw new WxPayException("签名错误！");
    }

    /**
     * 格式化参数格式化成url参数
     */
    private
    function ToUrlParams()
    {
        $buff = "";
        foreach ($this->values as $k => $v) {
            if ($k != "sign" && $v != "" && !is_array($v)) {
                $buff .= $k . "=" . $v . "&";
            }
        }
        $buff = trim($buff, "&");
        return $buff;
    }

    /**
     * 格式支付参数
     * @param unknown $data
     * @return string
     */
    private
    function payToUrlParams($data)
    {
        $buff = "";
        foreach ($data as $k => $v) {
            if ($k != "sign" && $v != "" && !is_array($v)) {
                $buff .= $k . "=" . $v . "&";
            }
        }

        $buff = trim($buff, "&");
        return $buff;
    }

    /**
     * 生成签名
     * @return 签名，本函数不覆盖sign成员变量，如要设置签名需要调用SetSign方法赋值
     */
    private
    function MakeSign($WEIXIN_KEY)
    {
        //签名步骤一：按字典序排序参数
        ksort($this->values);
        $string = $this->ToUrlParams();
        //签名步骤二：在string后加入KEY
        $string = $string . "&key=" . $WEIXIN_KEY;
        //签名步骤三：MD5加密
        $string = md5($string);

        //签名步骤四：所有字符转为大写
        $result = strtoupper($string);
        return $result;
    }

    /**
     * 支付签名
     * @return string
     */
    private
    function paySign($data, $WEIXIN_KEY)
    {
        //签名步骤一：按字典序排序参数
        ksort($data);
        $string = $this->payToUrlParams($data);
        //签名步骤二：在string后加入KEY
        $string = $string . "&key=" . $WEIXIN_KEY;
        //签名步骤三：MD5加密
        $string = md5($string);

        //签名步骤四：所有字符转为大写
        $result = strtoupper($string);
        return $result;
    }
}